package cn.uestc.labhms.modules.hms.service.impl;

import cn.uestc.labhms.modules.hms.dao.HmsChiEngDao;
import cn.uestc.labhms.modules.hms.dao.SqlMapper;
import cn.uestc.labhms.modules.hms.dao.ViewDao;
import cn.uestc.labhms.modules.hms.entity.HmsChiEngEntity;
import cn.uestc.labhms.modules.hms.entity.Vo.CommonConditionVo;
import cn.uestc.labhms.modules.hms.entity.Vo.ConditionsFindVo;
import cn.uestc.labhms.modules.hms.entity.Vo.ViewVo;
import cn.uestc.labhms.modules.hms.exception.HMSException;
import cn.uestc.labhms.modules.hms.service.ViewService;
import cn.uestc.labhms.modules.hms.util.HMSPageUtils;
import cn.uestc.labhms.modules.hms.util.TableUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Mu.tz
 */
@Service
public class ViewServiceImpl implements ViewService {

    private ConcurrentHashMap<String, Map<String, String>> cache = new ConcurrentHashMap<>();

    @Autowired
    private HmsChiEngDao hmsChiEngDao;

    private final Integer pageSize = 40;
    @Autowired
    private SqlMapper sqlMapper;
    @Autowired
    private ViewDao viewDao;
    @Override
    public HMSPageUtils getViewData(ViewVo viewVo) {
        // 判断权限
//        this.hasPermitted("*:*:*");
        String sql = viewDao.getSql(viewVo.getViewName());
        if (StringUtils.isEmpty(sql)) {
            throw new HMSException("不存在该视图");
        }
        // 获取数据数量
        int cnt = sqlMapper.selectExec(sql).size();
        if (sql.charAt(sql.length() - 1) == ';') {
            sql = sql.substring(0, sql.length() - 1);
        }
        int offset = (viewVo.getCurPage() - 1) * pageSize;
        sql += " limit " + pageSize + " offset " + offset + ";";
        List<LinkedHashMap<String, String>> maps = sqlMapper.selectExec(sql);
        List<Map<String, String>> resC = new ArrayList<>(maps.size());
        // 将查询出的英文字段转为中文字段，用于前端渲染，如果缓存中没有该视图的数据结构，则缓存
        for (Map<String, String> tmpMap : maps) {
            resC.add(this.convertHeadE2C(tmpMap, viewVo.getViewName()));
        }
        return new HMSPageUtils(resC, cnt, pageSize, viewVo.getCurPage(), viewVo.getViewName());
    }
    @Override
    public List<String> getViewNameList() {
        return viewDao.getViewNameList();
    }

    @Override
    public CommonConditionVo getViewField(String viewName) {
        if (!cache.containsKey(viewName)) {
            ViewVo viewVo = new ViewVo(1, viewName);
            this.getViewData(viewVo);
        }
        Set<String> fieldSet = cache.get(viewName).keySet();
        List<String> fieldList = new ArrayList<>(fieldSet);
        CommonConditionVo commonConditionVo = new CommonConditionVo();
        commonConditionVo.setConditionsCN(fieldList);
        return commonConditionVo;
    }

    @Override
    public HMSPageUtils getViewWithField(String viewName, List<String> keys, List<String> vals, Integer curPage) {
        if (ObjectUtils.isEmpty(viewName) || ObjectUtils.isEmpty(keys) || ObjectUtils.isEmpty(vals) || ObjectUtils.isEmpty(curPage)) {
            throw new HMSException("页数、查询键值对、视图名均不可为空");
        }
        String sql = viewDao.getSql(viewName);
        if (StringUtils.isEmpty(sql)) {
            throw new HMSException("不存在该视图");
        }
        if (sql.charAt(sql.length() - 1) == ';') {
            sql = sql.substring(0, sql.length() - 1);
        }
        List<String> keysE = new ArrayList<>();
        // 缓存中不存在表数据则先加载缓存
        if (!cache.containsKey(viewName)) {
            ViewVo viewVo = new ViewVo(1, viewName);
            this.getViewData(viewVo);
        }
        Map<String, String> fieldC2E = cache.get(viewName);
        for (String keyC : keys) {
            keysE.add(fieldC2E.get(keyC));
        }
        StringBuilder sqlSb = new StringBuilder(sql);
        sqlSb.insert(0, "select * from (");
        sqlSb.append(") as tmp where ");
        for (int i = 0; i < keysE.size(); i++) {
            sqlSb.append(keysE.get(i));
            sqlSb.append("='").append(vals.get(i)).append("' ");
            if (i != keysE.size() - 1) {
                sqlSb.append("and ");
            }
        }
        sql = sqlSb.toString();
        int offset = (curPage - 1) * pageSize;
        // 获取数据数量
        int cnt = sqlMapper.selectExec(sql).size();
        sql += " limit " + pageSize + " offset " + offset + ";";
        List<LinkedHashMap<String, String>> maps = sqlMapper.selectExec(sql);
        List<Map<String, String>> resC = new ArrayList<>(maps.size());
        // 将查询出的英文字段转为中文字段，用于前端渲染，如果缓存中没有该视图的数据结构，则缓存
        for (Map<String, String> tmpMap : maps) {
            resC.add(this.convertHeadE2C(tmpMap, viewName));
        }
        return new HMSPageUtils(resC, cnt, pageSize, curPage, viewName);
    }

    /**
     * 将hash表的key转成中文，向缓存中保存视图的数据结构
     * @param map 具体的英文数据
     * @param viewName 视图名，用于维护缓存
     * @return
     */
    private Map<String, String> convertHeadE2C(Map<String, String> map, String viewName) {
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Map<String, String> ans = new LinkedHashMap<>();
        Map<String, String> viewStructure = new HashMap<>();
        for (Map.Entry<String, String> entry : entries) {
            String fieldE = entry.getKey();
            // TODO: 2022/3/1 随机返回了个head，可能存在问题，出现问题再改
            String fieldC = hmsChiEngDao.getDistinctFieldE2C(fieldE);
            ans.put(fieldC, entry.getValue());
            viewStructure.put(fieldC, fieldE);
        }
        // DCL实现缓存视图数据结构
        if (!cache.containsKey(viewName)) {
            synchronized (this) {
                if (!cache.containsKey(viewName)) {
                    cache.put(viewName, viewStructure);
                }
            }
        }
        return ans;
    }

    /**
     * 判断是否有操作权限
     * @param perm 权限字符串
     */
    private void hasPermitted(String perm) {
        Subject subject = SecurityUtils.getSubject();
        if (!subject.isPermitted(perm)) {
            throw new AuthorizationException("无权限");
        }
    }
}
